package com.izhar.answeringmachine.service.Impl;

import com.izhar.answeringmachine.Entity.RunFail;
import com.izhar.answeringmachine.Entity.SubmitResult;
import com.izhar.answeringmachine.service.IRun;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.List;
import java.util.concurrent.*;

@Service
@Slf4j
public class RunImpl implements IRun {

    /**
     * base目录  用户方面更换windows和linux系统下的用户代码存放路径
     */
    @Value("${question.filePath}")
    private String baseLocation;

    public static final String TIME_OUT = "Time out";


    @Override
    public String run(String userId, String questionId, List<String> parameter, SubmitResult submitResult) {
        //执行代码运行线程
        CodeRunThread2 codeRunThread = new CodeRunThread2(parameter,userId,questionId,baseLocation);

        //线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        //计时运行
        Future<String> future = executorService.submit(codeRunThread);
        Process process = null;

        try {
            //运行用户代码，时长1s
            return future.get(1, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            //代码运行其他异常
            log.error("code error:"+e.getMessage());
            submitResult.setResultCode("5");
            submitResult.setErrorDetail(e.getMessage());
            //throw new RuntimeException("code error");

        } catch (ExecutionException e) {
            if (e.getCause() instanceof TimeoutException){
                //代码运行超时
                log.error("time out");
                submitResult.setResultCode("1");
                submitResult.setErrorDetail("time out");
                //throw new RuntimeException("time out  error");

            }
            //超出内存限制
            else if (e.getMessage().contains("code heap space")){
                log.error("code heap space");
                submitResult.setResultCode("2");
                submitResult.setErrorDetail("code heap space");
                //throw new RuntimeException("code heap space");
            }else{
                //其他错误
                log.error("code error: "+e.getMessage());
                submitResult.setResultCode("5");
                submitResult.setErrorDetail(e.getMessage());
                //throw new RuntimeException(e.getMessage());
            }

            //超时
        } catch (TimeoutException e) {
               log.error("time out");
                submitResult.setResultCode("1");
                submitResult.setErrorDetail("time out");
               //throw new RuntimeException("time out  error");
        }finally {
            //关闭运行代码的线程
            codeRunThread.getProcess().destroy();
            //关闭线程池
            executorService.shutdownNow();
        }
        return null;
    }
}

//代码运行时间测试线程
class CodeRunThread2 implements Callable<String>{

    //参数
    private List<String> parameters;

    //代码运行成功 cmd打印信息
    private String runSuccessInfo = "";

    //代码运行失败 cmd打印信息
    private String runErrorMsg = "";
    //用户id
    private String userId;
    //题号
    private String questionId;
    //用户代码临时文件路径
    private String location;

    private Process process;


    public CodeRunThread2(List<String> parameters, String userId, String questionId, String location) {
        this.parameters = parameters;
        this.userId = userId;
        this.questionId = questionId;
        this.location = location;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    public CodeRunThread2() {
    }

    public List<String> getParameters() {
        return parameters;
    }

    public void setParameters(List<String> parameters) {
        this.parameters = parameters;
    }

    public String getRunSuccessInfo() {
        return runSuccessInfo;
    }

    private void setRunSuccessInfo(String runSuccessInfo) {
        this.runSuccessInfo = runSuccessInfo;
    }

    public String getRunErrorMsg() {
        return runErrorMsg;
    }

    private void setRunErrorMsg(String runErrorMsg) {
        this.runErrorMsg = runErrorMsg;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getQuestionId() {
        return questionId;
    }


    public void setQuestionId(String questionId) {
        this.questionId = questionId;
    }

    public Process getProcess() {
        return process;
    }

    private void setProcess(Process process) {
        this.process = process;
    }

    @Override
    public String toString() {
        return "CodeRunThread2{" +
                "parameters=" + parameters +
                ", runSuccessInfo='" + runSuccessInfo + '\'' +
                ", runErrorMsg='" + runErrorMsg + '\'' +
                ", userId='" + userId + '\'' +
                ", questionId='" + questionId + '\'' +
                ", location='" + location + '\'' +
                '}';
    }



    @Override
    public String call() throws TimeoutException, RuntimeException, IOException {

        //初始化输出
        this.runSuccessInfo = "";
        this.runErrorMsg ="";
        //用户代码临时文件存放目录
        String location = this.location+this.userId+"_"+this.questionId+"\\";

        //java 执行class文件  -Xmx128m 虚拟机最大堆内存128m
        String[] run = {"java","-Xmx128m","Question"};

        Process process = Runtime.getRuntime().exec(run,null,new File(location));
        this.process = process;

        InputStream in = process.getInputStream();           //读取cmd的信息
        OutputStream out = process.getOutputStream();       //写数据到cmd
        InputStream err = process.getErrorStream();         //读取错误信息


        //如果代码有输入参数，则往运行程序输入参数，没参数则直接跳过
        if (parameters!=null && parameters.size()>0){
            //写入参数
            for (String str : parameters) {
                out.write(str.getBytes());
                out.write(" ".getBytes());
            }
            //写入换行
            out.write("\n".getBytes());
            out.flush();
            out.close();
        }


        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        BufferedReader errReader = new BufferedReader(new InputStreamReader(err));

        String line1 = "";
        while ((line1 = reader.readLine())!=null){
            this.runSuccessInfo+=line1+"\n";
        }
        this.runSuccessInfo = runSuccessInfo.trim();


        String line2 = "";
        while ((line2 = errReader.readLine())!=null) {
            this.runErrorMsg+=line2+"\n";
        }
        this.runErrorMsg = runErrorMsg.trim();

        //代码成功运行或运行失败，返回成功的， 失败的运行，抛异常
        if (runSuccessInfo!=null && !"".equals(runSuccessInfo.trim())) {
            return runSuccessInfo;
        } else if (runErrorMsg.contains("Java heap space")){
            throw new RuntimeException("code heap space");      //内存占用超出限制
        }else {
            throw new RuntimeException("user code run error: "+runErrorMsg);
        }
    }
}
